//
//  TCDynamicObject.m
//  CBLDemo
//
//  Created by cdk on 14-1-1.
//  Copyright (c) 2014年 Everimaging. All rights reserved.
//

#import "TCDynamicObject.h"
#import <ctype.h>
#import <objc/runtime.h>


#pragma mark - SELECTOR-TO-PROPERTY NAME MAPPING:


NS_INLINE BOOL isGetter(const char* name) {
    if (!name[0] || name[0]=='_' || name[strlen(name)-1] == ':')
        return NO;                    // If it has parameters it's not a getter
    if (strncmp(name, "get", 3) == 0)
        return NO;                    // Ignore "getXXX" variants of getter syntax
    return YES;
}

NS_INLINE BOOL isSetter(const char* name) {
    return strncmp("set", name, 3) == 0 && name[strlen(name)-1] == ':';
}


@implementation TCDynamicObject


// The Objective-C runtime calls this method when it's asked about a method that isn't natively
// implemented by this class. The implementation should either call class_addMethod and return YES,
// or return NO.
+ (BOOL)resolveInstanceMethod:(SEL)sel {
    const char *name = sel_getName(sel);
    NSString* key;
    Class declaredInClass;
    const char *propertyType;
    char signature[5];
    IMP accessor = NULL;
    
    if (isSetter(name)) {
        // choose an appropriately typed generic setter function.
        for (int upperCase=NO; upperCase<=YES; upperCase++) {
            key = [self setterKey:sel upperCase:upperCase];
            if (getPropertyInfo(self, key, YES, &declaredInClass, &propertyType)) {
                strcpy(signature, "v@: ");
                signature[3] = propertyType[0];
                accessor = [self impForSetterOfProperty: key ofType: propertyType];
                break;
            }
        }
    } else if (isGetter(name)) {
        // choose an appropriately typed getter function.
        key = [self getterKey:sel];
        if (getPropertyInfo(self, key, NO, &declaredInClass, &propertyType)) {
            strcpy(signature, " @:");
            signature[0] = propertyType[0];
            accessor = [self impForGetterOfProperty: key ofType: propertyType];
        }
    } else {
        // Not a getter or setter name.
        return NO;
    }
    
    if (accessor) {
        LogTo(MYDynamicObject, @"Creating dynamic accessor method -[%@ %s]", declaredInClass, name);
        class_addMethod(declaredInClass, sel, accessor, signature);
        return YES;
    }
    
    if (propertyType) {
        Warn(@"Dynamic property %@.%@ has type '%s' unsupported by %@",
             self, key, propertyType, self);
    }
    return NO;
}


@end
